﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the GNU General Public License version 2 (GPLv2).
// See accompanying file License.txt or copy at http://phuse.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Phuse.Net.Forums
{
    using System.Collections.Generic;
    using System.Data.Linq;
    using System.Data.Linq.Mapping;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text;

    /// <summary>See <see cref="IHost"/>.</summary>
    internal partial class Host : IHost, IHostInfo
    {
        private string dataDirectory;

        /// <summary>See <see cref="IHost.AccountName"/>.</summary>
        public string AccountName
        {
            get
            {
                return Decrypt(this.AccountNameEncrypted, this.Root.CryptoProvider);
            }

            set
            {
                this.AccountNameEncrypted = Encrypt(value, this.Root.CryptoProvider);
            }
        }

        /// <summary>See <see cref="IHost.Password"/>.</summary>
        public string Password
        {
            get
            {
                return Decrypt(this.PasswordEncrypted, this.Root.CryptoProvider);
            }

            set
            {
                this.PasswordEncrypted = Encrypt(value, this.Root.CryptoProvider);
            }
        }

        /// <summary>See <see cref="IDeletablePersistentObject.Delete"/>.</summary>
        public void Delete()
        {
            this.AssertValid();

            foreach (var group in this.Root.GetGroups(this).ToList())
            {
                group.Delete();
            }

            var entity = new OutboundArticleDelete() { HostId = this.Id };
            var root = this.Root;
            root.Attach(entity);
            root.DeleteOnSubmit(entity);
            root.InsertOnSubmit(new DeleteFile() { Path = this.DataDirectory });
            root.DeleteOnSubmit(this);
            root.ReopenDatabaseAfterCommit();
        }

        public bool Equals(IHostInfo other)
        {
            return this.Equals(other as Host);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>See <see cref="IHost.AddGroup"/>.</summary>
        IGroup IHost.AddGroup(
            string groupName,
            string groupDisplayName,
            int newInboundArticlesReceiveLimit,
            int cachedInboundArticlesLimit)
        {
            return this.AddGroup(
                groupName, groupDisplayName, newInboundArticlesReceiveLimit, cachedInboundArticlesLimit);
        }

        /// <summary>See <see cref="IHost.Groups"/>.</summary>
        IEnumerable<IGroup> IHost.Groups
        {
            get
            {
                this.AssertValid();
                return this.Root.GetGroups(this).Cast<IGroup>();
            }
        }

        /// <summary>See <see cref="IHost.AddOutboundArticle"/>.</summary>
        IOutboundArticle IHost.AddOutboundArticle()
        {
            return this.AddOutboundArticle();
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        internal Root Root
        {
            get { return EntityHelper.AssertNotDeleted(this.RootCore); }
            set { this.RootCore = value; }
        }

        internal string DataDirectory
        {
            get
            {
                if (this.dataDirectory == null)
                {
                    this.dataDirectory = Path.Combine("Out", this.Id.ToString(CultureInfo.InvariantCulture));
                }

                return this.dataDirectory;
            }
        }

        internal Group AddGroup(
            string groupName,
            string groupDisplayName,
            int newInboundArticlesReceiveLimit,
            int cachedInboundArticlesLimit)
        {
            this.AssertValid();

            var group = new Group()
            {
                Host = this,
                Name = groupName,
                DisplayName = groupDisplayName,
                NewInboundArticlesReceiveLimit = newInboundArticlesReceiveLimit,
                CachedInboundArticlesLimit = cachedInboundArticlesLimit
            };

            this.Root.InsertOnSubmit(group);
            return group;
        }

        internal OutboundArticle AddOutboundArticle()
        {
            var article = new OutboundArticle() { Host = this };
            this.Root.InsertOnSubmit(article);
            return article;
        }

        internal IEnumerable<OutboundArticle> GetOutboundArticles(SendStatus sendStatus)
        {
            return this.Root.GetOutboundArticles(this, sendStatus);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        private void AssertValid()
        {
            this.Root.AssertValid();
        }

        private bool Equals(Host other)
        {
            return (other != null) && (this.Id == other.Id);
        }

        private static byte[] Encrypt(string plainText, ICryptoProvider cryptoProvider)
        {
            return cryptoProvider.Encrypt(plainText == null ? null : Encoding.UTF8.GetBytes(plainText));
        }

        private static string Decrypt(byte[] cypherText, ICryptoProvider cryptoProvider)
        {
            byte[] decrypted = cryptoProvider.Decrypt(cypherText);
            return decrypted == null ? null : Encoding.UTF8.GetString(decrypted, 0, decrypted.Length);
        }

        /// <summary>The is fake entity is used to delete all outbound articles associated with a particular host.
        /// </summary>
        [Table(Name = "OutboundArticle")]
        private sealed class OutboundArticleDelete
        {
            private EntityRef<Host> host;

            ////////////////////////////////////////////////////////////////////////////////////////////////////////////

            [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called through reflection.")]
            [Column(IsPrimaryKey = true)]
            internal int HostId { get; set; }

            // This association is necessary to ensure that OutboundArticle rows are deleted *before* their referenced Host row
            [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called through reflection.")]
            [Association(Name = "Host_OutboundArticle2", Storage = "host", ThisKey = "HostId", IsForeignKey = true)]
            internal Host Host
            {
                get { return this.host.Entity; }
                set { this.host.Entity = value; }
            }
        }
    }
}
